#include <string>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <dlfcn.h>
#include <dirent.h>
#include <error.h>
#include "ModMgr.h"

using namespace std;

#define IODP_MODULE_MAX 32
#define IODP_MODULE_PATH "./iodp/"

typedef CIodp *(*fnCreate)();

CModMgr::CModMgr()
{
    m_pModule = nullptr;
    m_nModNum = 0;
}

CModMgr::~CModMgr()
{
    Destroy();
}

bool CModMgr::Init()
{
    bool bRet = false;
    int iLen = IODP_MODULE_MAX * sizeof(IodpModule);

    m_pModule = (IodpModule *)malloc(iLen);

    if (nullptr == m_pModule)
        return false;

    memset(m_pModule, 0, iLen);
    bRet = LoadModule((char *)IODP_MODULE_PATH);

    return bRet;
}

void CModMgr::Destroy()
{
    if (nullptr != m_pModule)
    {
        IodpModule *pModule = m_pModule;

        for (size_t i = 0; i < m_nModNum; i++)
        {
            // 释放共享内存,
            // 此处不释放，由CShmMgr统一释放，以免每个进程都释放一次，造成错误
            // if (nullptr != pModule->pShm)
            // {
            //     CShmMgr *pShmMgr = CShmMgr::GetInstance();

            //     if (nullptr == pShmMgr)
            //         printf("Error CShmMgr::GetInstance\n");

            //     pShmMgr->DestroyShmBlock(pModule->pShm);
            //     pModule->pShm = nullptr;
            // }

            if (nullptr != pModule->pIodp)
            {
                pModule->pIodp->Destroy();
                delete pModule->pIodp;
                pModule->pIodp = nullptr;
            }

            if (nullptr != pModule->pHandle)
            {
                dlclose(pModule->pHandle);
                pModule->pHandle = nullptr;
            }

            pModule++;
        }

        free(m_pModule);
        m_pModule = nullptr;
    }
}

bool CModMgr::LoadModule(char *pDirPath)
{
    bool bRet = false;
    char *pExt = nullptr;
    IodpModule *pModule = m_pModule;

    if (nullptr == pDirPath)
        return false;

    if (nullptr == m_pModule)
        return false;

    struct stat s;
    lstat(pDirPath, &s);

    if (!S_ISDIR(s.st_mode))
        return false;

    DIR *pDir = nullptr;
    struct dirent *pFileName = nullptr;

    pDir = opendir(pDirPath);

    if (nullptr == pDir)
    {
        printf("Error: iodp dir.\n");
        return false;
    }

    while (nullptr != (pFileName = readdir(pDir)))
    {
        if (0 == strcmp(pFileName->d_name, ".") || 0 == strcmp(pFileName->d_name, ".."))
            continue;

        if (DT_REG == pFileName->d_type)
        {
            pExt = strrchr(pFileName->d_name, '.');

            if (nullptr == pExt)
                continue;

            if (0 != strcmp(pExt, ".so"))
                continue;

            string strPath(pDirPath);
            strPath += pFileName->d_name;
            bRet = LoadIodp(strPath.c_str(), pModule);

            if (bRet)
            {
                pModule->iId = m_nModNum;
                m_nModNum++;
                pModule++;
            }
        }
    }

    closedir(pDir);

    return (0 < m_nModNum);
}

bool CModMgr::LoadIodp(const char *path, IodpModule *pModule)
{
    uint32_t nSize = 0;
    CIodp *pIodp = nullptr;
    CShmMgr *pShmMgr = nullptr;

    if (nullptr == path || nullptr == pModule)
        return false;

    pModule->pHandle = dlopen(path, RTLD_LAZY);
    // pModule->pHandle = dlopen(path, RTLD_NOW);

    if (nullptr == pModule->pHandle)
    {
        printf("dlopen error:%s\n", dlerror());
        return false;
    }

    fnCreate CreateIodp = (fnCreate)dlsym(pModule->pHandle, "Create");

    if (nullptr == CreateIodp)
        goto FAILED_LABLE;

    pModule->pIodp = CreateIodp();

    if (nullptr == pModule->pIodp)
        goto FAILED_LABLE;

    pIodp = pModule->pIodp;
    pModule->pName = pIodp->GetModuleName();
    nSize = pIodp->GetShmSize();
    pShmMgr = CShmMgr::GetInstance();

    if (nullptr == pShmMgr)
        goto FAILED_LABLE;

    pModule->pShm = pShmMgr->CreateShmBlock(nSize);

    if (nullptr == pModule->pShm)
        goto FAILED_LABLE;

    if (!pIodp->Init(pModule->pShm))
        goto FAILED_LABLE;

    printf("Success: load module %s\tshm:%d\n", path, nSize);

    return true;

FAILED_LABLE:
    if (nullptr != pModule->pIodp)
    {
        pIodp->Destroy();
        pModule->pIodp = nullptr;
    }

    if (nullptr != pModule->pHandle)
    {
        dlclose(pModule->pHandle);
        pModule->pHandle = nullptr;
    }

    return false;
}

int8_t CModMgr::GetModuleId(char *pName)
{
    int iRet = 0;
    IodpModule *pModule = nullptr;

    for (size_t i = 0; i < m_nModNum; i++)
    {
        pModule = m_pModule + i;
        iRet = strncmp(pName, pModule->pName, strlen(pModule->pName) + 1);

        if (0 == iRet)
            return pModule->iId;
    }

    return -1;
}

CIodp *CModMgr::GetIodp(int8_t iMod)
{
    if (0 > iMod || iMod >= m_nModNum)
        return nullptr;

    return (m_pModule + iMod)->pIodp;
}